﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Framework.Comum;
using Framework.Comum.DAL;
using NaviCar.DB.Repositorio;
using System.Linq.Expressions;
using NaviCar.DB;
using Framework.Comum.Pesquisa;
using NaviCar.Servico.Query;
using System.Reflection;

namespace NaviCar.Servico.Entidades
{
    public class ServicoBase<TEntity> where TEntity:class, new()
    {
        public ServicoBase()
        {
            repositorio = (IRepositorio<TEntity>)new RepositorioBase<TEntity>();
        }

        protected IRepositorio<TEntity> repositorio;

        public void Incluir(TEntity entidade)
        {
            this.repositorio.Incluir(entidade);
            this.repositorio.Salvar();
        }

        public void Detach(TEntity entidade)
        {
            this.repositorio.Detach(entidade);
        }

        public void Excluir(params object[] ids)
        {
            this.repositorio.Excluir(ids);
        }

        public void Alterar(TEntity entidade)
        {
            this.repositorio.Alterar(entidade);
            this.repositorio.Salvar();
        }

        public TEntity RecuperarPorID(params object[] ids)
        {
            return this.repositorio.RecuperarPorID(ids);
        }

        public IEnumerable<TEntity> RecuperarTodos()
        {
            return this.repositorio.RecuperarTodos();
        }

        public IEnumerable<TEntity> Recuperar(Expression<Func<TEntity, bool>> filtro = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> ordenar = null,
            string incluirPropriedades = "")
        {
            return this.repositorio.Recuperar(filtro,ordenar,incluirPropriedades);
        }

        public IQueryable<TEntity> Recuperar(IEnumerable<Framework.Comum.Pesquisa.CampoPesquisa> parametros, out int quantidadeTotal)
        {
            IQueryable<TEntity> query = this.repositorio.RecuperarTodos();
            foreach (CampoPesquisa campo in parametros)
            {
                query = MontarParametro(query, campo);
            }

            quantidadeTotal = query.Count();

            return query;
        }

        public IQueryable<TEntity> Recuperar(IEnumerable<Framework.Comum.Pesquisa.CampoPesquisa> parametros, string ordenar, int pagina, int quantidade, out int quantidadeTotal)
        {

            if (pagina < 0) pagina = 1;

            int pular = ((pagina - 1) * quantidade);

            IQueryable<TEntity> query = this.Recuperar(parametros, out quantidadeTotal);

            if (!string.IsNullOrEmpty(ordenar)) { query = query.OrderBy(ordenar); } else { query = query.OrderBy("0"); }
            if(pular > 0) {query = query.Skip(pular);}
            if (quantidade > 0) { query = query.Take(quantidade); }

            return query;
        }

        public int Salvar()
        {
            return this.repositorio.Salvar();
        }

        public void Configurar()
        {
            if (repositorio == null)
            {
                this.repositorio = new RepositorioBase<TEntity>();
            }
        }
        
        public void Dispose()
        {
            this.repositorio.Dispose();
        }

        public IQueryable Repositorio(string entidade)
        {
            return this.repositorio.Repositorio(entidade);
        }


        private IQueryable<TEntity> MontarParametro(IQueryable<TEntity> query, CampoPesquisa campo)
        {
            IQueryable<TEntity> retorno = null;

            Type typeParameterType = typeof(TEntity);

            string auxQuery = string.Empty;                
            Type tipo = typeof(TEntity);

            switch (campo.TipoPesquisa)
            {
                case TipoPesquisa.Igual:
                    auxQuery = campo.Campo + " = @0";
                    break;
                case TipoPesquisa.Like:
                    auxQuery = campo.Campo + string.Format(".StartsWith(\"{0}\")",campo.Valores[0]);
                    break;
                case TipoPesquisa.Maior:
                    auxQuery = campo.Campo + " > @0";
                    break;
                case TipoPesquisa.Menor:
                    auxQuery = campo.Campo + " < @0";
                    break;
                case TipoPesquisa.MaiorIgual:
                    auxQuery = campo.Campo + " >= @0";
                    break;
                case TipoPesquisa.MenorIgual:
                    auxQuery = campo.Campo + " <= @0";
                    break;
                case TipoPesquisa.Between:
                    if (!string.IsNullOrEmpty(campo.Valores[0]))
                    {
                        auxQuery = campo.Campo + " >= @0";
                    }

                    if (!string.IsNullOrEmpty(campo.Valores[1]))
                    {
                        auxQuery += campo.Campo2 + " <= @0";
                    }

                    break;
                case TipoPesquisa.In:
                    auxQuery = campo.Campo + " IN (@0)";

                    break;
                case TipoPesquisa.Custom:
                    auxQuery = campo.Query;
                    break;
            }
            
            PropertyInfo propriedade1 = query.ElementType.GetProperty(campo.Campo);
            PropertyInfo propriedade2 = null;
            object valor1 = null;
            object valor2 = null;

            valor1 = Convert.ChangeType(campo.Valores[0], propriedade1.PropertyType);
            if (!string.IsNullOrEmpty(campo.Campo2))
            {
                propriedade2 = query.ElementType.GetProperty(campo.Campo2);
                valor2 = Convert.ChangeType(campo.Valores[1], propriedade2.PropertyType);
            }

            retorno = query.Where(auxQuery, valor1, valor2);  

            return retorno;
        }


    }
}